#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <stdarg.h>
#include<cstdlib>
#include "LockGuard.hpp"
using namespace ThreadMoudle;

namespace bit
{
    // 日志等级：
    enum log_Grade
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    // 日志消息
    struct log_message
    {
        std::string _leval;    // 日志等级
        pid_t _id;             // 进程id
        std::string _filename; // 日志信息存储文件
        size_t _filenum;       // 日志信息所在行数
        std::string _cur_time; // 表示当前时间
        std::string _content;  // 日志内容

        std::string level_to_string(int level)
        {
            std::string ret;
            switch (level)
            {
            case DEBUG:
                ret = "DEBUG";
                break;
            case INFO:
                ret = "INFO";
                break;
            case WARNING:
                ret = "WARNING";
                break;
            case ERROR:
                ret = "ERROR";
                break;
            case FATAL:
                ret = "FATAL";
                break;
            default:
                ret = "UNKNOWN";
            }
            return ret;
        }
        std::string get_curtime()
        {
            char ret[128];

            // 先获取当前的一个时间戳;
            time_t now = time(nullptr);
            struct tm *cur_time = localtime(&now);

            // 以特定格式存储：%02d的意思是占据两个像素点，不足用0填充：
            snprintf(ret, sizeof(ret), "%d-%02d-%02d %02d:%02d:%02d",
                     cur_time->tm_year + 1900,
                     cur_time->tm_mon + 1,
                     cur_time->tm_mday,
                     cur_time->tm_hour,
                     cur_time->tm_min,
                     cur_time->tm_sec);
            return std::string(ret);
        }
    };

    #define Screen_Type 1
    #define File_Type 2

    const std::string default_filename = "log.txt";
    class Log
    {
    private:
        void _FlushtoScreen()
        {
            printf("[日志等级]：%s\n[进程id号]：%d\n[filename]：%s\n[所在行数]：%zd\n[日志时间]：%s\n[日志内容]：%s\n\n",
                   _lg._leval.c_str(), _lg._id, _lg._filename.c_str(), _lg._filenum, _lg._cur_time.c_str(), _lg._content.c_str());
        }
        void _FlushtoFile(const std::string& filename)
        {
            //C语言的文件操作：
            FILE* file = fopen(filename.c_str(), "a");
            fprintf(file, "[日志等级]：%s\n[进程id号]：%d\n[filename]：%s\n[所在行数]：%zd\n[日志时间]：%s\n[日志内容]：%s\n\n",
                   _lg._leval.c_str(), _lg._id, _lg._filename.c_str(), _lg._filenum, _lg._cur_time.c_str(), _lg._content.c_str());
            fclose(file);
        }
        void _Flush()
        {
            //文本的过滤功能：
            if(_type == File_Type && (_lg._leval == "ERROR" || _lg._leval == "FATAL"))
            {
                _FlushtoFile("error_log.txt");
            }

            Lock lo(&_lock);
            switch (_type)
            {
            case Screen_Type:
                _FlushtoScreen();
                break;
            case File_Type:
                _FlushtoFile(_logfile);
                break;
            }
        }

    public:
        // 构造函数：
        Log(const std::string logfile = default_filename) : _type(Screen_Type), _logfile(logfile)
        {
            // 对锁的初始化：
            pthread_mutex_init(&_lock, nullptr);
        }
        // 实现切换打印日志位置的功能：
        void shift()
        {
            if (_type ^ Screen_Type == 0)
                _type = File_Type;
            else
                _type = Screen_Type;
        }
        //清空文件内容:
        void clean()
        {
            FILE* file = fopen(_logfile.c_str(), "w");
            fclose(file);
        }

        void logMessage(std::string filename, int filenum, int level, const char *format, ...)
        {
            // 难点一：如何处理日志等级：
            _lg._leval = _lg.level_to_string(level);

            _lg._id = getpid();
            _lg._filename = filename;
            _lg._filenum = filenum;

            // 难点二：获取当前时间：
            _lg._cur_time = _lg.get_curtime();

            // 难点三：获取可变参数的值：
            char buff[1024];
            va_list va;

            // 1.初始化：
            va_start(va, format);

            // 2.处理：
            vsnprintf(buff, sizeof(buff), format, va);

            // 3.销毁：
            va_end(va);

            _lg._content = buff;
            _Flush();
        }
        // 析构函数：
        ~Log()
        {
            // 释放锁的资源：
            pthread_mutex_destroy(&_lock);
        }

    private:
        log_message _lg;
        int _type;             // 未来想往哪里打印；
        std::string _logfile;  // logfile日志文件，由用户指定；
        pthread_mutex_t _lock; // 互斥锁保证多线程访问日志，日志信息不会糅杂在一块
    };

    //更为优雅的设计：
    //定义一个全局的Log对象;
    Log lg;
    //##的作用是当可变参数为空时，去掉前面的逗号，避免语法错误（函数的可变参数不需要这种处理）
    #define LOG(Level, Format, ...)do{\
        lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__);\
    }while(0);

    #define SHIFT()do{\
        lg.shift();\
    }while(0)

    #define CLEAN()do{\
        lg.clean();\
    }while(0)
}